home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / ohlfind.zip / PRED.C < prev    next >
C/C++ Source or Header  |  1990-07-03  |  18KB  |  748 lines

  1. /* The Predicates and associated routines for Find.
  2.    Copyright (C) 1987, 1990 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 1, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. #include <stdio.h>
  19. #include <sys/types.h>
  20. #include <sys/stat.h>
  21. #include <pwd.h>
  22. struct passwd *getpwuid ();
  23. #include <grp.h>
  24. struct group *getgrgid ();
  25. #ifndef USG
  26. #include <strings.h>
  27. #else
  28. #include <string.h>
  29. #define index strchr
  30. #define rindex strrchr
  31. #endif
  32. #include "defs.h"
  33.  
  34. int fork ();
  35. int wait ();
  36.  
  37. boolean pred_and ();
  38. boolean pred_atime ();
  39. boolean pred_close ();
  40. boolean pred_ctime ();
  41. /* no pred_depth */
  42. boolean pred_exec ();
  43. boolean pred_fstype ();
  44. /* no pred_fulldays */
  45. boolean pred_group ();
  46. boolean pred_inum ();
  47. boolean pred_links ();
  48. boolean pred_ls ();
  49. boolean pred_mtime ();
  50. boolean pred_name ();
  51. boolean pred_negate ();
  52. boolean pred_newer ();
  53. boolean pred_nogroup ();
  54. boolean pred_nouser ();
  55. boolean pred_ok ();
  56. boolean pred_open ();
  57. boolean pred_or ();
  58. boolean pred_perm ();
  59. boolean pred_permmask ();
  60. boolean pred_print ();
  61. boolean pred_prune ();
  62. boolean pred_regex ();
  63. boolean pred_size ();
  64. boolean pred_type ();
  65. boolean pred_user ();
  66. /* no pred_version */
  67. /* no pred_xdev */
  68.  
  69. boolean launch ();
  70. char *basename ();
  71. char *filesystem_type ();
  72. void list_file ();
  73.  
  74. #ifdef    DEBUG
  75. struct pred_assoc
  76. {
  77.   PFB pred_func;
  78.   char *pred_name;
  79. };
  80.  
  81. struct pred_assoc pred_table[] =
  82. {
  83.   {pred_and, "and     "},
  84.   {pred_atime, "atime   "},
  85.   {pred_close, ")       "},
  86.   {pred_ctime, "ctime   "},
  87.   {pred_exec, "exec    "},
  88.   {pred_fstype, "fstype  "},
  89.   {pred_group, "group   "},
  90.   {pred_inum, "inum    "},
  91.   {pred_links, "links   "},
  92.   {pred_ls, "ls      "},
  93.   {pred_mtime, "mtime   "},
  94.   {pred_name, "name    "},
  95.   {pred_negate, "!       "},
  96.   {pred_newer, "newer   "},
  97.   {pred_nogroup, "nogroup "},
  98.   {pred_nouser, "nouser  "},
  99.   {pred_ok, "ok      "},
  100.   {pred_open, "(       "},
  101.   {pred_or, "or      "},
  102.   {pred_perm, "perm    "},
  103.   {pred_permmask, "permmask"},
  104.   {pred_print, "print   "},
  105.   {pred_prune, "prune   "},
  106.   {pred_regex, "regex   "},
  107.   {pred_size, "size    "},
  108.   {pred_type, "type    "},
  109.   {pred_user, "user    "},
  110.   {0, "none    "}
  111. };
  112.  
  113. struct op_assoc
  114. {
  115.   short type;
  116.   char *type_name;
  117. };
  118.  
  119. struct op_assoc type_table[] =
  120. {
  121.   {NO_TYPE, "no_type    "},
  122.   {VICTIM_TYPE, "victim_type    "},
  123.   {UNI_OP, "uni_op    "},
  124.   {BI_OP, "bi_op    "},
  125.   {OPEN_PAREN, "open_paren    "},
  126.   {CLOSE_PAREN, "close_paren    "},
  127.   {-1, "unknown    "}
  128. };
  129.  
  130. struct prec_assoc
  131. {
  132.   short prec;
  133.   char *prec_name;
  134. };
  135.  
  136. struct prec_assoc prec_table[] =
  137. {
  138.   {NO_PREC, "no_prec     "},
  139.   {OR_PREC, "or_prec     "},
  140.   {AND_PREC, "and_prec    "},
  141.   {NEGATE_PREC, "negate_prec "},
  142.   {MAX_PREC, "max_prec    "},
  143.   {-1, "unknown    "}
  144. };
  145. #endif    /* DEBUG */
  146.  
  147. /* Predicate processing routines.
  148.  
  149.    PATHNAME is the full pathname of the file being checked.
  150.    *STAT_BUF contains information about PATHNAME.
  151.    *PRED_PTR contains information for applying the predicate.
  152.  
  153.    Return true if the file passes this predicate, false if not. */
  154.  
  155. boolean
  156. pred_and (pathname, stat_buf, pred_ptr)
  157.      char *pathname;
  158.      struct stat *stat_buf;
  159.      struct pred_struct *pred_ptr;
  160. {
  161.   if ((*pred_ptr->pred_left->pred_func) (pathname, stat_buf,
  162.                      pred_ptr->pred_left))
  163.     return ((*pred_ptr->pred_right->pred_func) (pathname, stat_buf,
  164.                         pred_ptr->pred_right));
  165.   else
  166.     return (false);
  167. }
  168.  
  169. boolean
  170. pred_atime (pathname, stat_buf, pred_ptr)
  171.      char *pathname;
  172.      struct stat *stat_buf;
  173.      struct pred_struct *pred_ptr;
  174. {
  175. #ifdef    DEBUG
  176.   printf ("pred_atime: checking %s %ld %s", pathname, stat_buf->st_atime,
  177.       ctime (&stat_buf->st_atime));
  178. #endif    /* DEBUG */
  179.   switch (pred_ptr->args.info.kind)
  180.     {
  181.     case COMP_GT:
  182.       if (stat_buf->st_atime > pred_ptr->args.info.l_val)
  183.     return (true);
  184.       break;
  185.     case COMP_LT:
  186.       if (stat_buf->st_atime < pred_ptr->args.info.l_val)
  187.     return (true);
  188.       break;
  189.     case COMP_EQ:
  190.       if ((stat_buf->st_atime >= pred_ptr->args.info.l_val)
  191.       && (stat_buf->st_atime < pred_ptr->args.info.l_val
  192.           + DAYSECS))
  193.     return (true);
  194.       break;
  195.     }
  196.   return (false);
  197. }
  198.  
  199. boolean
  200. pred_close (pathname, stat_buf, pred_ptr)
  201.      char *pathname;
  202.      struct stat *stat_buf;
  203.      struct pred_struct *pred_ptr;
  204. {
  205.   error (0, 0, "oops -- got into pred_close!");
  206.   return (true);
  207. }
  208.  
  209. boolean
  210. pred_ctime (pathname, stat_buf, pred_ptr)
  211.      char *pathname;
  212.      struct stat *stat_buf;
  213.      struct pred_struct *pred_ptr;
  214. {
  215.   switch (pred_ptr->args.info.kind)
  216.     {
  217.     case COMP_GT:
  218.       if (stat_buf->st_ctime > pred_ptr->args.info.l_val)
  219.     return (true);
  220.       break;
  221.     case COMP_LT:
  222.       if (stat_buf->st_ctime < pred_ptr->args.info.l_val)
  223.     return (true);
  224.       break;
  225.     case COMP_EQ:
  226.       if ((stat_buf->st_ctime >= pred_ptr->args.info.l_val)
  227.       && (stat_buf->st_ctime < pred_ptr->args.info.l_val
  228.           + DAYSECS))
  229.     return (true);
  230.       break;
  231.     }
  232.   return (false);
  233. }
  234.  
  235. boolean
  236. pred_exec (pathname, stat_buf, pred_ptr)
  237.      char *pathname;
  238.      struct stat *stat_buf;
  239.      struct pred_struct *pred_ptr;
  240. {
  241.   int i, path_pos;
  242.  
  243.   for (path_pos = 0, i = pred_ptr->args.exec_vec.path_loc[0];
  244.        i != -1;
  245.        path_pos++, i = pred_ptr->args.exec_vec.path_loc[path_pos])
  246.     pred_ptr->args.exec_vec.vec[i] = pathname;
  247.   return (launch (pred_ptr));
  248. }
  249.  
  250. boolean
  251. pred_fstype (pathname, stat_buf, pred_ptr)
  252.      char *pathname;
  253.      struct stat *stat_buf;
  254.      struct pred_struct *pred_ptr;
  255. {
  256.   char *fstype;
  257.  
  258.   fstype = filesystem_type (stat_buf);
  259.   if (fstype && strcmp (fstype, pred_ptr->args.str) == 0)
  260.     return (true);
  261.   return (false);
  262. }
  263.  
  264. boolean
  265. pred_group (pathname, stat_buf, pred_ptr)
  266.      char *pathname;
  267.      struct stat *stat_buf;
  268.      struct pred_struct *pred_ptr;
  269. {
  270.   if (pred_ptr->args.gid == stat_buf->st_gid)
  271.     return (true);
  272.   else
  273.     return (false);
  274. }
  275.  
  276. boolean
  277. pred_inum (pathname, stat_buf, pred_ptr)
  278.      char *pathname;
  279.      struct stat *stat_buf;
  280.      struct pred_struct *pred_ptr;
  281. {
  282.   switch (pred_ptr->args.info.kind)
  283.     {
  284.     case COMP_GT:
  285.       if (stat_buf->st_ino > pred_ptr->args.info.l_val)
  286.     return (true);
  287.       break;
  288.     case COMP_LT:
  289.       if (stat_buf->st_ino < pred_ptr->args.info.l_val)
  290.     return (true);
  291.       break;
  292.     case COMP_EQ:
  293.       if (stat_buf->st_ino == pred_ptr->args.info.l_val)
  294.     return (true);
  295.       break;
  296.     }
  297.   return (false);
  298. }
  299.  
  300. boolean
  301. pred_links (pathname, stat_buf, pred_ptr)
  302.      char *pathname;
  303.      struct stat *stat_buf;
  304.      struct pred_struct *pred_ptr;
  305. {
  306.   switch (pred_ptr->args.info.kind)
  307.     {
  308.     case COMP_GT:
  309.       if (stat_buf->st_nlink > pred_ptr->args.info.l_val)
  310.     return (true);
  311.       break;
  312.     case COMP_LT:
  313.       if (stat_buf->st_nlink < pred_ptr->args.info.l_val)
  314.     return (true);
  315.       break;
  316.     case COMP_EQ:
  317.       if (stat_buf->st_nlink == pred_ptr->args.info.l_val)
  318.     return (true);
  319.       break;
  320.     }
  321.   return (false);
  322. }
  323.  
  324. boolean
  325. pred_ls (pathname, stat_buf, pred_ptr)
  326.      char *pathname;
  327.      struct stat *stat_buf;
  328.      struct pred_struct *pred_ptr;
  329. {
  330.   list_file (pathname, stat_buf);
  331.   return (true);
  332. }
  333.  
  334. boolean
  335. pred_mtime (pathname, stat_buf, pred_ptr)
  336.      char *pathname;
  337.      struct stat *stat_buf;
  338.      struct pred_struct *pred_ptr;
  339. {
  340.   switch (pred_ptr->args.info.kind)
  341.     {
  342.     case COMP_GT:
  343.       if (stat_buf->st_mtime > pred_ptr->args.info.l_val)
  344.     return (true);
  345.       break;
  346.     case COMP_LT:
  347.       if (stat_buf->st_mtime < pred_ptr->args.info.l_val)
  348.     return (true);
  349.       break;
  350.     case COMP_EQ:
  351.       if ((stat_buf->st_mtime >= pred_ptr->args.info.l_val)
  352.       && (stat_buf->st_mtime < pred_ptr->args.info.l_val
  353.           + DAYSECS))
  354.     return (true);
  355.       break;
  356.     }
  357.   return (false);
  358. }
  359.  
  360. boolean
  361. pred_name (pathname, stat_buf, pred_ptr)
  362.      char *pathname;
  363.      struct stat *stat_buf;
  364.      struct pred_struct *pred_ptr;
  365. {
  366.   char *just_fname;
  367.  
  368.   just_fname = basename (pathname);
  369.   if (glob_match (pred_ptr->args.str, just_fname, 1))
  370.     return (true);
  371.   return (false);
  372. }
  373.  
  374. boolean
  375. pred_negate (pathname, stat_buf, pred_ptr)
  376.      char *pathname;
  377.      struct stat *stat_buf;
  378.      struct pred_struct *pred_ptr;
  379. {
  380.   return (!(*pred_ptr->pred_left->pred_func) (pathname, stat_buf,
  381.                           pred_ptr->pred_left));
  382. }
  383.  
  384. boolean
  385. pred_newer (pathname, stat_buf, pred_ptr)
  386.      char *pathname;
  387.      struct stat *stat_buf;
  388.      struct pred_struct *pred_ptr;
  389. {
  390.   if (stat_buf->st_mtime > pred_ptr->args.time)
  391.     return (true);
  392.   return (false);
  393. }
  394.  
  395. boolean
  396. pred_nogroup (pathname, stat_buf, pred_ptr)
  397.      char *pathname;
  398.      struct stat *stat_buf;
  399.      struct pred_struct *pred_ptr;
  400. {
  401.   return getgrgid (stat_buf->st_gid) == NULL;
  402. }
  403.  
  404. boolean
  405. pred_nouser (pathname, stat_buf, pred_ptr)
  406.      char *pathname;
  407.      struct stat *stat_buf;
  408.      struct pred_struct *pred_ptr;
  409. {
  410.   return getpwuid (stat_buf->st_uid) == NULL;
  411. }
  412.  
  413. boolean
  414. pred_ok (pathname, stat_buf, pred_ptr)
  415.      char *pathname;
  416.      struct stat *stat_buf;
  417.      struct pred_struct *pred_ptr;
  418. {
  419.   int i, yes, path_pos;
  420.  
  421.   for (path_pos = 0, i = pred_ptr->args.exec_vec.path_loc[0];
  422.        i != -1;
  423.        path_pos++, i = pred_ptr->args.exec_vec.path_loc[path_pos])
  424.     pred_ptr->args.exec_vec.vec[i] = pathname;
  425.   fprintf (stderr, "< %s ... %s > ? ",
  426.        pred_ptr->args.exec_vec.vec[0], pathname);
  427.   fflush (stderr);
  428.   i = getchar ();
  429.   yes = (i == 'y' || i == 'Y');
  430.   while (i != EOF && i != '\n')
  431.     i = getchar ();
  432.   if (yes)
  433.     return (launch (pred_ptr));
  434.   else
  435.     return (false);
  436. }
  437.  
  438. boolean
  439. pred_open (pathname, stat_buf, pred_ptr)
  440.      char *pathname;
  441.      struct stat *stat_buf;
  442.      struct pred_struct *pred_ptr;
  443. {
  444.   error (0, 0, "oops -- got into pred_open!");
  445.   return (true);
  446. }
  447.  
  448. boolean
  449. pred_or (pathname, stat_buf, pred_ptr)
  450.      char *pathname;
  451.      struct stat *stat_buf;
  452.      struct pred_struct *pred_ptr;
  453. {
  454.   if (!(*pred_ptr->pred_left->pred_func) (pathname, stat_buf,
  455.                       pred_ptr->pred_left))
  456.     return ((*pred_ptr->pred_right->pred_func) (pathname, stat_buf,
  457.                         pred_ptr->pred_right));
  458.   else
  459.     return (true);
  460. }
  461.  
  462. boolean
  463. pred_perm (pathname, stat_buf, pred_ptr)
  464.      char *pathname;
  465.      struct stat *stat_buf;
  466.      struct pred_struct *pred_ptr;
  467. {
  468.   if (pred_ptr->args.perm & 010000)
  469.     {
  470.       /* Magic flag set in parse_perm: compare suid, sgid, sticky bits as well;
  471.      also, true if at least the given bits are set. */
  472.       if ((stat_buf->st_mode & 07777 & perm_mask & pred_ptr->args.perm)
  473.       == (pred_ptr->args.perm & 07777))
  474.     return (true);
  475.     }
  476.   else
  477.     {
  478.       if ((stat_buf->st_mode & 0777 & perm_mask) == pred_ptr->args.perm)
  479.     return (true);
  480.     }
  481.   return (false);
  482. }
  483.  
  484. boolean
  485. pred_permmask (pathname, stat_buf, pred_ptr)
  486.      char *pathname;
  487.      struct stat *stat_buf;
  488.      struct pred_struct *pred_ptr;
  489. {
  490.   perm_mask = pred_ptr->args.perm;
  491.   return (true);
  492. }
  493.  
  494. boolean
  495. pred_print (pathname, stat_buf, pred_ptr)
  496.      char *pathname;
  497.      struct stat *stat_buf;
  498.      struct pred_struct *pred_ptr;
  499. {
  500.   puts (pathname);
  501.   return (true);
  502. }
  503.  
  504. boolean
  505. pred_prune (pathname, stat_buf, pred_ptr)
  506.      char *pathname;
  507.      struct stat *stat_buf;
  508.      struct pred_struct *pred_ptr;
  509. {
  510.   stop_at_current_level = true;
  511.   return (do_dir_first);    /* This is what SunOS find seems to do. */
  512. }
  513.  
  514. boolean
  515. pred_regex (pathname, stat_buf, pred_ptr)
  516.      char *pathname;
  517.      struct stat *stat_buf;
  518.      struct pred_struct *pred_ptr;
  519. {
  520.   if (re_match (pred_ptr->args.regex, pathname, strlen (pathname), 0,
  521.         (struct re_registers *) NULL) != -1)
  522.     return (true);
  523.   return (false);
  524. }
  525.  
  526. boolean
  527. pred_size (pathname, stat_buf, pred_ptr)
  528.      char *pathname;
  529.      struct stat *stat_buf;
  530.      struct pred_struct *pred_ptr;
  531. {
  532.   unsigned long f_val;
  533.  
  534.   if (pred_ptr->args.size.block)
  535.     f_val = (stat_buf->st_size + BLKSIZE - 1) / BLKSIZE;
  536.   else
  537.     f_val = stat_buf->st_size;
  538.   switch (pred_ptr->args.size.kind)
  539.     {
  540.     case COMP_GT:
  541.       if (f_val > pred_ptr->args.size.size)
  542.     return (true);
  543.       break;
  544.     case COMP_LT:
  545.       if (f_val < pred_ptr->args.size.size)
  546.     return (true);
  547.       break;
  548.     case COMP_EQ:
  549.       if (f_val == pred_ptr->args.size.size)
  550.     return (true);
  551.       break;
  552.     }
  553.   return (false);
  554. }
  555.  
  556. boolean
  557. pred_type (pathname, stat_buf, pred_ptr)
  558.      char *pathname;
  559.      struct stat *stat_buf;
  560.      struct pred_struct *pred_ptr;
  561. {
  562.   if ((stat_buf->st_mode & S_IFMT) == pred_ptr->args.type)
  563.     return (true);
  564.   else
  565.     return (false);
  566. }
  567.  
  568. boolean
  569. pred_user (pathname, stat_buf, pred_ptr)
  570.      char *pathname;
  571.      struct stat *stat_buf;
  572.      struct pred_struct *pred_ptr;
  573. {
  574.   if (pred_ptr->args.uid == stat_buf->st_uid)
  575.     return (true);
  576.   else
  577.     return (false);
  578. }
  579.  
  580. boolean
  581. launch (pred_ptr)
  582.      struct pred_struct *pred_ptr;
  583. {
  584.   int status, wait_ret, child_pid;
  585.  
  586.   /*  1) fork to get a child; parent remembers the child pid
  587.       2) child execs the command requested
  588.       3) parent waits, with stat_loc non_zero
  589.       check for proper pid of child
  590.       Possible returns:
  591.    
  592.       ret    errno    status(h)   status(l)
  593.       pid    x    signal#        0177    stopped
  594.       pid    x    exit arg    0        term by exit or _exit
  595.       pid    x    0        signal #    term by signal
  596.       -1    EINTR                parent got signal
  597.       -1    other                some other kind of error
  598.       
  599.       Return true only if the pid matches, status(l) is
  600.       zero, and the exit arg (status high) is 0.
  601.       Otherwise return false, possibly printing an error message. */
  602.  
  603.   child_pid = fork ();
  604.   if (child_pid == -1)
  605.     error (1, errno, "cannot fork");
  606.   if (child_pid == 0)
  607.     {
  608.       /* We be the child. */
  609.       execvp (pred_ptr->args.exec_vec.vec[0], pred_ptr->args.exec_vec.vec);
  610.       error (1, errno, "%s", pred_ptr->args.exec_vec.vec[0]);
  611.     }
  612.   wait_ret = wait (&status);
  613.   if (wait_ret == -1)
  614.     {
  615.       error (0, errno, "error waiting for child process");
  616.       exit_status = 1;
  617.       return (false);
  618.     }
  619.   if (wait_ret != child_pid)
  620.     {
  621.       error (0, 0, "wait saw another child, pid %d", wait_ret);
  622.       error (0, 0, "expected child pid %d; status: %d %d",
  623.          child_pid, status >> 8, status & 0xff);
  624.       exit_status = 1;
  625.       return (false);
  626.     }
  627.   if (status & 0xff == 0177)
  628.     {
  629.       error (0, 0, "child stopped; status %d %d\n",
  630.          status >> 8, status & 0xff);
  631.       exit_status = 1;
  632.       return (false);
  633.     }
  634.   if (status & 0xff != 0)
  635.     {
  636.       error (0, 0, "child terminated abnormally; status %d %d",
  637.          status >> 8, status & 0xff);
  638.       exit_status = 1;
  639.       return (false);
  640.     }
  641.   return (!(status >> 8));
  642. }
  643.  
  644. #ifdef    DEBUG
  645. /* Return a pointer to the string representation of 
  646.    the predicate function PRED_FUNC. */
  647.  
  648. char *
  649. find_pred_name (pred_func)
  650.      PFB pred_func;
  651. {
  652.   int i;
  653.  
  654.   for (i = 0; pred_table[i].pred_func != 0; i++)
  655.     if (pred_table[i].pred_func == pred_func)
  656.       break;
  657.   return (pred_table[i].pred_name);
  658. }
  659.  
  660. char *
  661. type_name (type)
  662.      short type;
  663. {
  664.   int i;
  665.  
  666.   for (i = 0; type_table[i].type != (short) -1; i++)
  667.     if (type_table[i].type == type)
  668.       break;
  669.   return (type_table[i].type_name);
  670. }
  671.  
  672. char *
  673. prec_name (prec)
  674.      short prec;
  675. {
  676.   int i;
  677.  
  678.   for (i = 0; prec_table[i].prec != (short) -1; i++)
  679.     if (prec_table[i].prec == prec)
  680.       break;
  681.   return (prec_table[i].prec_name);
  682. }
  683.  
  684. /* Walk the expression tree NODE to stdout.
  685.    INDENT is the number of levels to indent the left margin. */
  686.  
  687. void
  688. print_tree (node, indent)
  689.      struct pred_struct *node;
  690.      int indent;
  691. {
  692.   int i;
  693.  
  694.   if (node == NULL)
  695.     return;
  696.   for (i = 0; i < indent; i++)
  697.     printf ("    ");
  698.   printf ("%s %s %s %x\n", find_pred_name (node->pred_func),
  699.       type_name (node->p_type), prec_name (node->p_prec), node);
  700.   for (i = 0; i < indent; i++)
  701.     printf ("    ");
  702.   printf ("left:\n");
  703.   print_tree (node->pred_left, indent + 1);
  704.   for (i = 0; i < indent; i++)
  705.     printf ("    ");
  706.   printf ("right:\n");
  707.   print_tree (node->pred_right, indent + 1);
  708. }
  709.  
  710. /* Copy STR into BUF and trim blanks from the end of BUF.
  711.    Return BUF. */
  712.  
  713. char *
  714. blank_rtrim (str, buf)
  715.      char *str;
  716.      char *buf;
  717. {
  718.   int i;
  719.  
  720.   if (str == NULL)
  721.     return (NULL);
  722.   strcpy (buf, str);
  723.   i = strlen (buf) - 1;
  724.   while ((i >= 0) && ((buf[i] == ' ') || buf[i] == '\t'))
  725.     i--;
  726.   buf[++i] = '\0';
  727.   return (buf);
  728. }
  729.  
  730. /* Print out the predicate list starting at NODE. */
  731.  
  732. void
  733. print_list (node)
  734.      struct pred_struct *node;
  735. {
  736.   struct pred_struct *cur;
  737.   char name[256];
  738.  
  739.   cur = node;
  740.   while (cur != NULL)
  741.     {
  742.       printf ("%s ", blank_rtrim (find_pred_name (cur->pred_func), name));
  743.       cur = cur->pred_next;
  744.     }
  745.   printf ("\n");
  746. }
  747. #endif    /* DEBUG */
  748.